package com.gcloud.mesh.analysis.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import com.gcloud.mesh.analysis.data.IDataCleaning;
import com.gcloud.mesh.analysis.data.PolynomialDataCleaning;
import com.gcloud.mesh.analysis.enums.MeterXType;
import com.gcloud.mesh.analysis.enums.MeterYType;
import com.gcloud.mesh.analysis.service.IDataCleaningService;
import com.gcloud.mesh.asset.enums.DeviceType;
import com.gcloud.mesh.asset.service.IAssetService;
import com.gcloud.mesh.header.enums.MonitorMeter;
import com.gcloud.mesh.header.exception.AnalysisErrorCode;
import com.gcloud.mesh.header.exception.BaseException;
import com.gcloud.mesh.header.msg.asset.ListDeviceMsg;
import com.gcloud.mesh.header.msg.monitor.StatisticsMsg;
import com.gcloud.mesh.header.vo.analysis.DataVo;
import com.gcloud.mesh.header.vo.asset.DeviceItemVo;
import com.gcloud.mesh.header.vo.monitor.StatisticsPointVo;
import com.gcloud.mesh.header.vo.monitor.StatisticsVo;
import com.gcloud.mesh.header.vo.supplier.SupplierVo;
import com.gcloud.mesh.header.vo.supplier.XbrotherSupervisionSystemVo;
import com.gcloud.mesh.monitor.service.StatisticsCheatService;
import com.gcloud.mesh.monitor.service.StatisticsService;
import com.gcloud.mesh.supplier.entity.SupplierEntity;
import com.gcloud.mesh.supplier.enums.SystemType;
import com.gcloud.mesh.supplier.service.SupplierService;
import com.gcloud.mesh.utils.SupplierSystemTypeUtil;
import com.gcloud.mesh.utils.TimestampUtil;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@ConditionalOnProperty(name = "mesh.analysis.data", havingValue = "reality")
public class DataCleaningServiceImpl implements IDataCleaningService {
	
	@Autowired
	private StatisticsService statisticsService;
	
	@Autowired
	StatisticsCheatService statisticsCheatService;
	
	@Autowired
	private IAssetService assetService;
	
	@Autowired
	private SupplierService supplierService;
	
	public static void main(String[] args) {
		
	}

	@Override
	public IDataCleaning acquireFitterData(Date startTime, Date endTime, String nodeId, String airId) {
		// TODO Auto-generated method stub
		PolynomialDataCleaning result = null;
		
		Map<String, DataVo> dataXMap = metricCleanData(startTime, endTime, MeterXType.SERVER_POWER_CONSUMPTION.getMeter().getMeter(), nodeId);
		
		Map<String, DataVo> dataYMap = metricCleanData(startTime, endTime, MeterXType.AIR_OUT_TEMP.getMeter().getMeter(), airId);
		
		result = mergeMetricCleanData(dataXMap, dataYMap);
		
		return result;
	}
	
	@Override
	public IDataCleaning acquireSimulateData(Date startTime, Date endTime, String dcId, String metricX) {
		// TODO Auto-generated method stub
		PolynomialDataCleaning result = null;
			
		ListDeviceMsg listDeviceMsg = new ListDeviceMsg();
		listDeviceMsg.setDatacenterId(dcId);
		
		if(MeterXType.getTypeByName(metricX) == MeterXType.SERVER_POWER_CONSUMPTION) {
			listDeviceMsg.setType(DeviceType.SERVER.getNo());
		}else if(MeterXType.getTypeByName(metricX) == MeterXType.AIR_OUT_TEMP || MeterXType.getTypeByName(metricX) == MeterXType.AIR_IN_TEMP) {
			listDeviceMsg.setType(DeviceType.AIR_CONDITION.getNo());
		}	
		List<DeviceItemVo> devicesVo = null;
		try {
			devicesVo = assetService.listDevice(listDeviceMsg);
		}catch(Exception e) {
			log.error("[DataCleaningService][acquireSimulateData] assetService的listDevice方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.ASSET_SERVICE_ERROR);
		}
		
		//①仿真因素数据清洗
		List<DataVo> data = metricMeanCleanData(startTime, endTime, metricX, devicesVo);
		
		//②pue数据清洗
		Map<String, Double> dataYMap = pueCleanData(dcId, startTime, endTime);
		
		//③去除因素、pue中的非法数据
		result = mergeMetricAndPueCleanData(data, dataYMap);

		return result;
	}

	@Override
	public String getAirCurrentTemp(String airId) {
		// TODO Auto-generated method stub
		String result = null;
		try {
			result = statisticsCheatService.latestSample(MonitorMeter.AIR_IN_TEMPERATURE.getMeter(), airId);
		}catch (Exception e) {
			log.error("[DataCleaningService][getAirCurrentTemp] statisticsService的latestSample方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.STATISTICS_SERVICE_ERROR);
		}
		return result;
	}

	@Override
	public String getAirSetTemp(String airId) {
		// TODO Auto-generated method stub
		String result = null;
		try {
			result = statisticsCheatService.latestSample(MonitorMeter.AIR_SETTING_TEMPERATURE.getMeter(), airId);
		}catch(Exception e) {
			log.error("[DataCleaningService][getAirSetTemp] statisticsService的latestSample方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.STATISTICS_SERVICE_ERROR);
		}
		return result;
	}

	@Override
	public String getAirOut(String airId) {
		// TODO Auto-generated method stub
		String result = null;
		try {
			result = statisticsCheatService.latestSample(MonitorMeter.AIR_OUT_TEMPERATURE.getMeter(), airId);
		}catch(Exception e) {
			log.error("[DataCleaningService][getAirOut] statisticsService的latestSample方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.STATISTICS_SERVICE_ERROR);
		}
		return result;
	}

	@Override
	public String getAirIn(String airId) {
		// TODO Auto-generated method stub
		String result = statisticsCheatService.latestSample(MonitorMeter.AIR_IN_TEMPERATURE.getMeter(), airId);
		return result;
	}
	
	@Override
	public String getAirHumidityOut(String airId) {
		// TODO Auto-generated method stub
		String result = null;
		try {
			result = statisticsCheatService.latestSample(MonitorMeter.AIR_OUT_HUMIDITY.getMeter(), airId);
		}catch(Exception e) {
			log.error("[DataCleaningService][getAirOut] statisticsService的latestSample方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.STATISTICS_SERVICE_ERROR);
		}
		return result;
	}

	@Override
	public String getAirHumidityIn(String airId) {
		// TODO Auto-generated method stub
		String result = statisticsCheatService.latestSample(MonitorMeter.AIR_IN_HUMIDITY.getMeter(), airId);
		return result;
	}

	@Override
	public String getPwrNode(String nodeId) {
		// TODO Auto-generated method stub
		String result = null;
		try {
			result = statisticsCheatService.latestSample(MonitorMeter.SERVER_POWER.getMeter(), nodeId);
		}catch (Exception e) {
			log.error("[DataCleaningService][getPwrNode] statisticsService的latestSample方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.STATISTICS_SERVICE_ERROR);
		}
		return result;
	}

	@Override
	public String geAirInTemp(String airId) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getAirOutTemp(String airId) {
		// TODO Auto-generated method stub
		return null;
	}
	
	private StatisticsVo getStaticsByDate(String resourceId, Date startTime, Date endTime, String meter) {
		StatisticsMsg msg = new StatisticsMsg();
		msg.setResourceId(resourceId);
		msg.setMeter(meter);
		msg.setBeginTime(TimestampUtil.dateToStr(startTime));
		msg.setEndTime(TimestampUtil.dateToStr(endTime));
		StatisticsVo statisticsVo = null;
		try {
			statisticsVo = statisticsCheatService.statistics(msg);
		}catch (Exception e) {
			log.error("[DataCleaningService][getStaticsByDate] statisticsService的statistics方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.STATISTICS_SERVICE_ERROR);
		}
		return statisticsVo;
	}
	
	private String getSupervisionByDc(String dcId) {
		List<SupplierEntity> suppliers = null;
		try {
			suppliers = supplierService.listByType(SystemType.SS.getName());
		}catch(Exception e) {
			log.error("[DataCleaningService][getSupervisionByDc] supplierService的listByType方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.SUPPLIER_SERVICE_ERROR);
		}
		SupplierVo vo = null;
    	for(SupplierEntity supplier: suppliers) {
    		if(supplier.getDatacenterId().equals(dcId)) {
    			XbrotherSupervisionSystemVo obj = (XbrotherSupervisionSystemVo)SupplierSystemTypeUtil.getByName(supplier.getType(), supplier.getConfig());
    			return obj.getHostIp();
    		}
    	}
    	return null;
	}
	
	private List<DataVo> metricMeanCleanData(Date startTime, Date endTime, String metricX, List<DeviceItemVo> devicesVo) {
		Map<String, List<DataVo>> dataMap = new LinkedHashMap<String, List<DataVo>>();
		for(DeviceItemVo vo: devicesVo) {
			StatisticsVo statistics = getStaticsByDate(vo.getId(), startTime, endTime, MeterXType.getTypeByName(metricX).getMeter().getMeter());
			if(statistics.getList().size() != 0 && statistics.getList().get(0) != null) {
				for(StatisticsPointVo pointVo: statistics.getList().get(0).getPoints()) {
					DataVo dataVo = new DataVo();
					String key = pointVo.getTime().substring(0, pointVo.getTime().length()-3);
					List<DataVo> data = dataMap.get(key);
					if(data == null) {
						data = new ArrayList<DataVo>();
						dataMap.put(key, data);
					}
					if(pointVo.getValue() != null && pointVo.getValue() != 0) {
						dataVo.setTimestamp(key);
						dataVo.setDataX(pointVo.getValue());
						data.add(dataVo);
					}
				}
			}	
		}
		List<DataVo> result = new ArrayList<DataVo>();
		for(Map.Entry<String, List<DataVo>> entry: dataMap.entrySet()) {
			if(entry.getValue().size() > 0) {
				DataVo dataVo = new DataVo();
				List<DataVo> data = entry.getValue();
				double avg = data.stream().collect(Collectors.averagingDouble(DataVo::getDataX));
				dataVo.setTimestamp(entry.getKey());
				dataVo.setDataX(avg);
				result.add(dataVo);
			}
		}	
		return result;
	}
	
	private Map<String, DataVo> metricCleanData(Date startTime, Date endTime, String metricX, String resourceId) {
		Map<String, DataVo> dataMap = new LinkedHashMap<String, DataVo>();
		StatisticsVo statistics = getStaticsByDate(resourceId, startTime, endTime, metricX);
		if(statistics.getList().size() != 0 && statistics.getList().get(0) != null) {
			for(StatisticsPointVo pointVo: statistics.getList().get(0).getPoints()) {
				DataVo data = new DataVo();
				String key = pointVo.getTime().substring(0, pointVo.getTime().length()-3);
				if(pointVo.getValue() != null && pointVo.getValue() != 0) {
					data.setTimestamp(key);
					data.setDataX(pointVo.getValue());
					dataMap.put(key, data);
				}
			}
		}	
		return dataMap;
	}
	
	private Map<String, Double> pueCleanData(String dcId, Date startTime, Date endTime) {
		String cabinetId = getSupervisionByDc(dcId);
		StatisticsVo statistics = getStaticsByDate(cabinetId, startTime, endTime, MonitorMeter.PUE.getMeter());
		Map<String, Double> dataYMap = new HashMap<String, Double>();
		if(statistics.getList().size() !=0 && statistics.getList().get(0) != null) {
			List<StatisticsPointVo> points = statistics.getList().get(0).getPoints();
			for(StatisticsPointVo pointVo: points) {
				String key = pointVo.getTime().substring(0, pointVo.getTime().length()-3);
				if(pointVo.getValue() != null && pointVo.getValue() != 0) {
					dataYMap.put(key, pointVo.getValue());
				}
			}
		}
		return dataYMap;
	}

	private PolynomialDataCleaning mergeMetricAndPueCleanData(List<DataVo> data, Map<String, Double> dataYMap) {
		PolynomialDataCleaning result = new PolynomialDataCleaning();
		for(DataVo dataVo: data) {
			Double dataY = dataYMap.get(dataVo.getTimestamp());
			if( dataY == null) {
				data.remove(dataVo);
			}
			dataVo.setDataY(dataY);
		}
		result.setData(data);
		return result;
	}
	
	private PolynomialDataCleaning mergeMetricCleanData(Map<String, DataVo> dataXMap, Map<String, DataVo> dataYMap) {
		PolynomialDataCleaning result = new PolynomialDataCleaning();
		List<DataVo> data = new ArrayList<DataVo>();
		for(Map.Entry<String, DataVo> entry: dataXMap.entrySet()) {
			DataVo dataY = dataYMap.get(entry.getKey());
			if(dataY != null) {
				DataVo vo = entry.getValue();
				vo.setDataY(dataY.getDataX());
				data.add(vo);
			}
		}
		result.setData(data);
		return result;
	}

	@Override
	public IDataCleaning acquireSimulateData(Date startTime, Date endTime, String dcId, String deviceId,
			String metricX) {
		// TODO Auto-generated method stub
		PolynomialDataCleaning result = null;
		
		//①仿真因素数据清洗
		Map<String, DataVo> dataXMap = metricCleanData(startTime, endTime, MeterXType.getTypeByName(metricX).getMeter().getMeter(), deviceId);
		
		//②pue数据清洗
		String cabinetId = getSupervisionByDc(dcId);
		Map<String, DataVo> dataYMap = metricCleanData(startTime, endTime, MeterYType.PUE.getMeter().getMeter(), cabinetId);
		
		//③去除因素、pue中的非法数据
		result = mergeMetricCleanData(dataXMap, dataYMap);

		return result;
	}

	@Override
	public String getUpsVoltageOut(String upsId) {
		// TODO Auto-generated method stub
		String result = null;
		try {
			result = statisticsCheatService.latestSample(MonitorMeter.UPS_OUT_VOLTAGE.getMeter(), upsId);
		}catch(Exception e) {
			log.error("[DataCleaningService][getUpsVoltageOut] statisticsService的latestSample方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.STATISTICS_SERVICE_ERROR);
		}
		return result;
	}

	@Override
	public String getUpsVoltageIn(String upsId) {
		// TODO Auto-generated method stub
		String result = null;
		try {
			result = statisticsCheatService.latestSample(MonitorMeter.UPS_IN_VOLTAGE.getMeter(), upsId);
		}catch(Exception e) {
			log.error("[DataCleaningService][getUpsVoltageIn] statisticsService的latestSample方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.STATISTICS_SERVICE_ERROR);
		}
		return result;
	}

	@Override
	public String getUpsTemp(String upsId) {
		// TODO Auto-generated method stub
		String result = null;
		try {
			result = statisticsCheatService.latestSample(MonitorMeter.UPS_TEMPERATURE.getMeter(), upsId);
		}catch(Exception e) {
			log.error("[DataCleaningService][getUpsTemp] statisticsService的latestSample方法访问异常: {}", e.getMessage());
			throw new BaseException(AnalysisErrorCode.STATISTICS_SERVICE_ERROR);
		}
		return result;
	}
}
